/**
 * Copyright (c) 2018-2022, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 * 
 * Contains: ipc system
 * 
 * Change Logs:
 * Date           Author            Notes
 * 2022-11-11     JasonHu           Init
 */

#include <nxos/syscall.h>
#include <nxos/ipc.h>
#include <nxos/utils.h>

NX_PRIVATE NX_Solt SysIpcConnect(const char * name, NX_IpcClientConfig * outConfig, NX_Size bufSize)
{
    NX_Solt solt = NX_SOLT_INVALID_VALUE;
    NX_Error err;
    NX_ErrorSet((err = NX_Syscall4(NX_API_IpcConnect, name, outConfig, bufSize, &solt)));
    return solt;
}

NX_PRIVATE NX_Size SysIpcCall(NX_Solt connectSolt, NX_IpcMsg * msg)
{
    NX_Size returnValue;
    NX_Error err;
    NX_ErrorSet((err = NX_Syscall3(NX_API_IpcCall, connectSolt, msg, &returnValue)));
    return returnValue;
}

NX_Solt NX_IpcBind(const char * name, NX_ServerHandler serverHandler, NX_Size maxClient)
{
    NX_Solt solt = NX_SOLT_INVALID_VALUE;
    NX_Error err;
    NX_ErrorSet((err = NX_Syscall4(NX_API_IpcBind, name, serverHandler, maxClient, &solt)));
    return solt;
}

NX_Error NX_IpcConnect(const char *name, NX_IpcClient * client, NX_Size bufSize)
{
    NX_Solt connect;

    NX_IpcClientConfig config = {0};

    connect = SysIpcConnect(name, &config, bufSize);

    if (connect == NX_SOLT_INVALID_VALUE)
    {
        return NX_ErrorGet();
    }

    client->sharedBuf = config.bufAddr;
    client->sharedBufLen = config.bufSize;
    client->connect = connect;

    return NX_EOK;
}

NX_Error NX_IpcReturn(NX_Size returnValue)
{
    NX_Error err;
    NX_ErrorSet((err = NX_Syscall1(NX_API_IpcReturn, returnValue)));
    return err;
}

NX_Size NX_IpcCall(NX_IpcClient * client, NX_IpcMsg * ipcMsg)
{
    return SysIpcCall(client->connect, ipcMsg);
}

char * NX_IpcGetMsgData(NX_IpcMsg *ipcMsg)
{
    return (char *)ipcMsg + ipcMsg->dataOffset;
}

NX_PRIVATE NX_Solt *IpcGetSoltPtr(NX_IpcMsg *ipc_msg, NX_Offset index)
{
    return (NX_Solt *)((char *)ipc_msg + ipc_msg->capSlotsOffset) + index;
}

NX_Solt NX_IpcGetMsgSolt(NX_IpcMsg *ipc_msg, NX_Offset index)
{
    if (index >= ipc_msg->soltCount)
        return NX_SOLT_INVALID_VALUE;
    return *IpcGetSoltPtr(ipc_msg, index);
}

NX_Error NX_IpcSetMsgSolt(NX_IpcMsg * ipc_msg, NX_Offset index, NX_Solt solt)
{
    if (index >= ipc_msg->soltCount)
        return NX_SOLT_INVALID_VALUE;
    *IpcGetSoltPtr(ipc_msg, index) = solt;
    return 0;
}

NX_IpcMsg * NX_IpcCreateMsg(NX_IpcClient * client, NX_Size dataLen, NX_Size soltCount)
{
    NX_IpcMsg *ipcMsg;
    int i;

    if (sizeof(*ipcMsg) + dataLen + (soltCount * sizeof(NX_Solt)) > client->sharedBufLen)
    {
        NX_Printf("ipcMsg: msg buf size %d out of range %d\n", 
            sizeof(*ipcMsg) + dataLen + (soltCount * sizeof(NX_Solt)), client->sharedBufLen);
        return NX_NULL;
    }

    ipcMsg = (NX_IpcMsg *)client->sharedBuf;
    ipcMsg->dataLen = dataLen;
    ipcMsg->soltCount = soltCount;

    ipcMsg->dataOffset = sizeof(*ipcMsg);
    ipcMsg->capSlotsOffset = ipcMsg->dataOffset + dataLen;
    NX_MemZero(NX_IpcGetMsgData(ipcMsg), dataLen);
    for (i = 0; i < soltCount; i++)
        NX_IpcSetMsgSolt(ipcMsg, i, NX_SOLT_INVALID_VALUE);

    return ipcMsg;
}

/* FIXME: currently ipc_msg is not dynamically allocated so that no need to free */
NX_Error NX_IpcDestroyMsg(NX_IpcMsg * ipc_msg)
{
    return NX_EOK;
}
